package com.z1.core.http;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.httpclient.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import com.z1.core.JackJsonUtil;
import com.z1.core.exception.Z1Exception;

public abstract class AbstractHttpDispatcher<T> {
	
	protected final String REST_CHARSET = "UTF-8";
	protected Class<T> tClass= null;
	
	protected int socketTimeout = 10000;
	protected int connectTimeout = 10000;
	protected int connectionRequestTimeout = 10000;
	
	@SuppressWarnings("unchecked")
	public AbstractHttpDispatcher() {
		Type[] types = ((ParameterizedType) getClass().getGenericSuperclass())
			.getActualTypeArguments();
		tClass = (Class<T>)types[0];
	}

	/**
	 * 创建HttpClient
	 * @return
	 */
	private final CloseableHttpClient openClient() {
		RequestConfig requestConfig = RequestConfig.custom()
			.setSocketTimeout(socketTimeout)
			.setConnectTimeout(connectTimeout)
			.setConnectionRequestTimeout(connectionRequestTimeout)
			.build();
		try {
			return HttpClients.custom()
					.setDefaultRequestConfig(requestConfig)
					.setSslcontext(createIgnoreVerifySSL())
					.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
					.build();
		} catch (KeyManagementException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	private final SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
		SSLContext sc = SSLContext.getInstance("TLSV1.2");
		X509TrustManager trustManager = new X509TrustManager() {
			@Override
			public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
			}

			@Override
			public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
			}

			@Override
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return null;
			}
		};

		sc.init(null, new TrustManager[] { trustManager }, null);
		return sc;
	}
	
	/**
	 * 执行请求
	 * @param request 请求对象
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private final T execute(HttpRequestBase request) {
		CloseableHttpClient client = null;
		CloseableHttpResponse response = null;
		try { 
			client = openClient();
			if (null == client) {
				throw new Z1Exception("open client is null");
			}
			response = client.execute(request);
			//响应状态
			int statusCode = response.getStatusLine().getStatusCode();
			String resultBuffer = EntityUtils.toString(response.getEntity(), REST_CHARSET);
			//异常：响应状态>=400
			if (HttpStatus.SC_BAD_REQUEST <= statusCode) {
				throw new Z1Exception(new StringBuffer("rest request err\n")
					.append("Url : " + request.getURI().toString() + "\n")
					.append("Method : " + request.getMethod() + "\n")
					.append("response : " + resultBuffer)
					.toString()
				);
			}
			else if (tClass == String.class) {
				return (T)resultBuffer;
			}
			else {
				return JackJsonUtil.parseToTFromString(resultBuffer, tClass);
			}
		}
		catch (Throwable e) { throw new Z1Exception(e); }
		finally {
			if (null != client) {
				try { client.close(); }
				catch (IOException e) { }
			}
		}
	}
	
	/**
	 * HTTP-GET请求
	 * @param url 请求地址
	 * @param paramMap 请求参数
	 * @param headMap 请求头
	 * @return
	 */
	public final T executeGet(String url, Map<String, String> paramMap, Map<String, String> headMap) {
		//组装URL
		StringBuffer buffer = new StringBuffer(UrlFormat.transfromUrl(url));
		if (null != paramMap) {
			for (String key : paramMap.keySet()) {
				buffer.append("&&")
					.append(key)
					.append("=")
					.append(paramMap.get(key));
			}
		}
		
		HttpGet get = new HttpGet(buffer.toString());
		
		//设置头Head
		if (null != headMap) {
			for (String key : headMap.keySet()) {
				String v = headMap.get(key);
				if (null == v) { continue; }
				get.setHeader(key, v);
			}
		}
		
		try {
			return execute(get);
		}
		catch (Throwable e) {
			throw new Z1Exception(
				new StringBuffer()
				.append("url : ").append(url).append("\t\n")
				.append("request : ").append(null == paramMap ? "null" : JackJsonUtil.toJSONString(paramMap))
				.toString(), e
			);
		}
	}
	
	/**
	 * HTTP-POST请求
	 * @param url 请求地址
	 * @param paramMap 请求参数
	 * @param headMap 请求头
	 * @return
	 */
	public final T executePost(String url, Map<String, String> paramMap, Map<String, String> headMap) {
		HttpPost post = new HttpPost(UrlFormat.transfromUrl(url));
		
		//设置参数
		if (null != paramMap) {
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			for (String key : paramMap.keySet()) {
				nvps.add(new BasicNameValuePair(key, String.valueOf(paramMap.get(key))));
			}
			try {
				post.setEntity(new UrlEncodedFormEntity(nvps, REST_CHARSET));
			}
			catch (Throwable e) {
				throw new Z1Exception("http post request paramMap err", e);
			}
			
		}
		//设置头Head
		if (null != headMap) {
			for (String key : headMap.keySet()) {
				String v = headMap.get(key);
				if (null == v) { continue; }
				post.setHeader(key, v);
			}
		}
		
		try {
			return execute(post);
		}
		catch (Throwable e) {
			throw new Z1Exception(
				new StringBuffer()
				.append("url : ").append(url).append("\t\n")
				.append("request : ").append(null == paramMap ? "null" : JackJsonUtil.toJSONString(paramMap))
				.toString(), e
			);
		}
	}
	
	/**
	 * TTP-POST请求
	 * @param url 请求地址
	 * @param paramString 请求参数
	 * @param headMap 请求头
	 * @return
	 */
	public final T executePost(String url, String paramString, Map<String, String> headMap) {
		HttpPost post = new HttpPost(UrlFormat.transfromUrl(url));
		
		//设置参数
		if (null != paramString) {
			StringEntity entity = new StringEntity(paramString, REST_CHARSET);
			//entity.setContentType(ContentType.APPLICATION_JSON.toString());
			post.setEntity(entity);
		}
		//设置头Head
		if (null != headMap) {
			for (String key : headMap.keySet()) {
				String v = headMap.get(key);
				if (null == v) { continue; }
				post.setHeader(key, v);
			}
		}
		
		try {
			return execute(post);
		}
		catch (Throwable e) {
			throw new Z1Exception(
				new StringBuffer()
				.append("url : ").append(url).append("\t\n")
				.append("request : ").append(null == paramString ? "null" : paramString)
				.toString(), e
			);
		}
	}
	
	/**
	 * HTTP-POST请求
	 * @param url 请求地址
	 * @param paramMap 请求参数
	 * @param headMap 请求头
	 * @return
	 */
	public final T executePut(String url, Map<String, String> paramMap, Map<String, String> headMap) {
		HttpPut put = new HttpPut(UrlFormat.transfromUrl(url));
		
		//设置参数
		if (null != paramMap) {
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			for (String key : paramMap.keySet()) {
				nvps.add(new BasicNameValuePair(key, String.valueOf(paramMap.get(key))));
			}
			try {
				put.setEntity(new UrlEncodedFormEntity(nvps, REST_CHARSET));
			}
			catch (Throwable e) {
				throw new Z1Exception("http post request paramMap err", e);
			}
			
		}
		//设置头Head
		if (null != headMap) {
			for (String key : headMap.keySet()) {
				String v = headMap.get(key);
				if (null == v) { continue; }
				put.setHeader(key, v);
			}
		}
		
		try {
			return execute(put);
		}
		catch (Throwable e) {
			throw new Z1Exception(
				new StringBuffer()
				.append("url : ").append(url).append("\t\n")
				.append("request : ").append(null == paramMap ? "null" : JackJsonUtil.toJSONString(paramMap))
				.toString(), e
			);
		}
	}
	
	/**
	 * HTTP-POST请求
	 * @param url 请求地址
	 * @param paramString 请求参数
	 * @param headMap 请求头
	 * @return
	 */
	public final T executePut(String url, String paramString, Map<String, String> headMap) {
		HttpPut put = new HttpPut(UrlFormat.transfromUrl(url));
		
		//设置参数
		if (null != paramString) {
			StringEntity entity = new StringEntity(paramString, REST_CHARSET);
			//entity.setContentType(ContentType.APPLICATION_JSON.toString());
			put.setEntity(entity);
		}
		//设置头Head
		if (null != headMap) {
			for (String key : headMap.keySet()) {
				String v = headMap.get(key);
				if (null == v) { continue; }
				put.setHeader(key, v);
			}
		}
		
		try {
			return execute(put);
		}
		catch (Throwable e) {
			throw new Z1Exception(
				new StringBuffer()
				.append("url : ").append(url).append("\t\n")
				.append("request : ").append(null == paramString ? "null" : paramString)
				.toString(), e
			);
		}
	}
	
	/**
	 * HTTP-DELETE请求
	 * @param url 请求地址
	 * @param paramMap 请求参数
	 * @param headMap 请求头
	 * @return
	 */
	public final T executeDelete(String url, Map<String, String> paramMap, Map<String, String> headMap) {
		//组装URL
		StringBuffer buffer = new StringBuffer(UrlFormat.transfromUrl(url));
		if (null != paramMap) {
			for (String key : paramMap.keySet()) {
				buffer.append("&&")
					.append(key)
					.append("=")
					.append(paramMap.get(key));
			}
		}
		
		HttpDelete delete = new HttpDelete(buffer.toString());
		
		//设置头Head
		if (null != headMap) {
			for (String key : headMap.keySet()) {
				String v = headMap.get(key);
				if (null == v) { continue; }
				delete.setHeader(key, v);
			}
		}
		
		try {
			return execute(delete);
		}
		catch (Throwable e) {
			throw new Z1Exception(
				new StringBuffer()
				.append("url : ").append(url).append("\t\n")
				.append("request : ").append(null == paramMap ? "null" : JackJsonUtil.toJSONString(paramMap))
				.toString(), e
			);
			
		}
	}
	
}
